package at.fhooe.stefanirndorfer.quadtree;

import at.fhooe.stefanirndorfer.entity.EntityManager;
import at.fhooe.stefanirndorfer.game.Game;
import at.fhooe.stefanirndorfer.logging.Log;

public class QuadTree {

	int minSize;
	int maxSize;
	int maxWallPoints;
	QuadTreeNode rootNode;
	
	private EntityManager em;
	
	//Debug
	int leafCounter;
	
	public QuadTree(int minSize, int maxSize, int maxWallPoints, double[] center){
		this.minSize = minSize;
		this.maxSize = maxSize;
		this.maxWallPoints = maxWallPoints;
		
		rootNode = new QuadTreeNode(maxSize, center);
		em = Game.getInstance().getEntityManager();
		leafCounter = 0;
		
		//perform tree generation
		generateTree(rootNode);
		Log.info(this, "Leaf-Counter: " + leafCounter);
	}

	
	/**
	 * this method uses the entity-manager to perform collision detections
	 * 
	 * @param center
	 */
	public void generateTree(QuadTreeNode node){
		
		double[] upperLeft = new double[]{node.getCenter()[0] - node.getSize()/2, node.getCenter()[1] + node.getSize()/2};
		double[] lowerRight = new double[]{node.getCenter()[0] + node.getSize()/2, node.getCenter()[1] - node.getSize()/2};
		
		//check recursion-anchor
		if(!floodFill(upperLeft, lowerRight)){
			
			node.setIsLeaf(false);
			
			int size = node.getSize()/2;
			if(size >= minSize){
				
				// sub-divide clockwise (1,2,3,4) and call generateTree
				
				//childnode 1
				QuadTreeNode childOne = new QuadTreeNode(size, new double[]{upperLeft[0] + size/2, upperLeft[1] - size/2});
				node.getChildNodes().add(childOne);
				generateTree(childOne);
				
				//childnode 2
				QuadTreeNode childTwo = new QuadTreeNode(size, new double[]{upperLeft[0] + size * 1.5, upperLeft[1] - size/2});
				node.getChildNodes().add(childTwo);
				generateTree(childTwo);
				
				//childnode 3
				QuadTreeNode childThree = new QuadTreeNode(size, new double[]{upperLeft[0] + size/2, upperLeft[1] - size * 1.5});
				node.getChildNodes().add(childThree);
				generateTree(childThree);
				
				//childnode 4
				QuadTreeNode childFour = new QuadTreeNode(size, new double[]{upperLeft[0] + size * 1.5, upperLeft[1] - size * 1.5});
				node.getChildNodes().add(childFour);
				generateTree(childFour);
				
			} 
			
		} else {
			leafCounter++;
			//Debug
			Log.info(this, "A Leaf: Upper Left X: " + upperLeft[0] + " Y: "+ upperLeft[1] + ", Lower Right X: " + lowerRight[0] + " Y: " + lowerRight[1]);

		}
		
	}
	
	
	/**
	 * returns false, if a Node must be subdivided
	 * subdivision is necessary if the sizes of the sub-nodes will be > minSize
	 * and the anchor-value of wall-coordinates was exceeded 
	 * 
	 * @param upperLeft
	 * @param lowerRight
	 * @return
	 */
	private boolean floodFill(double[] upperLeft, double[] lowerRight) {
		
		int size = (int)Math.abs(lowerRight[0] - upperLeft[0]);
		
		int wallPointCounter = 0;
		
		int uStartCoord = (int)upperLeft[0];
		int vStartCoord = (int)lowerRight[1];
		
		for (int u = uStartCoord; u < size + uStartCoord; u++) {
			for (int v = vStartCoord; v < size + vStartCoord; v++) {
				
				if(em.checkWallCollisions(u,v))
					wallPointCounter++;
				
				if(wallPointCounter > maxWallPoints){
					return false;
				}
			}
		}
		return true;
	}


	//////////////////////////////////////////////////
	// Getters and Setters
	//////////////////////////////////////////////////
	
	
	
	public int getMaxSize() {
		return maxSize;
	}

	public int getMinSize() {
		return minSize;
	}


	public void setMinSize(int minSize) {
		this.minSize = minSize;
	}


	public void setMaxSize(int maxSize) {
		this.maxSize = maxSize;
	}

	public int getAnchorInPixel() {
		return maxWallPoints;
	}

	public void setAnchorInPixel(int anchorInPixel) {
		this.maxWallPoints = anchorInPixel;
	}
	
	
	
}

